package Master;

/**
 * Project:    metsysdirg
 * File Name: Task.java
 * Change History:
 *     ********************************
 *     1.0     5/14/2011    Hengfeng Li
 *         Create the skeleton file
 *     ********************************
 *
 */

import Module.ResultUnit;
import Module.WorkService;

import java.io.IOException;

/**
 * The Task class is an information of task provided by
 * client, including the submit time, running time, cost,
 * and program's information of this task. It will be send
 * to the ScheduleAlgorithm to decide priority. After that
 * It will be transformed to a WorkerTaskUnit in WorkDispatcher,
 * which will be as an argument when invoking the remote work
 *
 */
public class Task implements TaskRunnable {
    /** The identification of this task*/
    private int taskID;

    /** The program name */
    private String programName;

    /** The argument of execute program */
    private String[] arguments;

    /** The path of program file */
    private String programPath;

    /** The output file name */
    private String outputFileName;

    /** The name of data file */
    private String datafileName;

    /** The path of data file */
    private String datafilePath;

    /** The worker resource in which this task will be running */
    private Resource resource;

    /** The estimate running time, a unit is a second */
    private float estimateTime;
    
    private float elapsedTime;
    
    private float totalCost;

    /*
    those information will be the important stuff of
    schedule algorithm, but now we just use the FCFS
    algorithm.

    private int cost;
    private int EstimateRunningTime;

    */

    /***
     * Initialize the WorkerTaskUnit which will be sent
     * to workers.
     * @param taskID Identification of workunit
     * @param programName Program name
     * @param arguments Program's argument
     */
    public Task(int taskID, String programName, String programPath,
                String[] arguments, String datafileName,
                String datafilePath, String outputFileName,
                float estimateRunningTime){
        this.taskID = taskID;
        this.programName = programName;
        this.arguments = arguments;
        this.programPath = programPath;
        this.outputFileName = outputFileName;
        this.datafileName = datafileName;
        this.datafilePath = datafilePath;
        this.estimateTime = estimateRunningTime;
        this.elapsedTime = 0;
        this.totalCost = 0;
    }

    public float getEstimateTime(){
        return estimateTime;
    }

    /* Get the id of this task */
    public int getTaskID(){
        return taskID;
    }

    /* Get the program name */
    public String getProgramName(){
        return programName;
    }

    /* Get the arugment of execute program */
    public String[] getArguments(){
        return arguments;
    }

    /* Get the path of program file */
    public String getProgramPath(){
        return programPath;
    }

    /* Get the name of output file */
    public String getOutputFileName(){
        return outputFileName;
    }

    /** Get the name of data file */
    public String getDatafileName(){
        return datafileName;
    }

    /** Get the path of data file */
    public String getDatafilePath(){
        return datafilePath;
    }

    /**
     * Read the program file
     */
    private void readProgramFile(WorkerTaskUnit workerTaskUnit){

        byte[] buffer;
		try {
			buffer = IO.readFile(getProgramPath());
			workerTaskUnit.setProgramBody(buffer);
		} catch (IOException e) {
			System.out.println("Error: "+e.getMessage());
		} 
    }

    /**
     * Read the program file
     * @throws IOException 
     */
    private void readDataFile(WorkerTaskUnit workerTaskUnit) {

        byte[] buffer;
		try {
			buffer = IO.readFile(getDatafilePath());
			workerTaskUnit.setDatafile(buffer);
		} catch (IOException e) {
			System.out.println("Error: "+e.getMessage());
		}
    }

    /**
     * Specify the resource where the task will be running.
     * @param resource
     */
    public void specifyResource(Resource resource){
        this.resource = resource;
    }

    /**
     * The WorkDispatcher should be a work thread
     * in the master-side. When there is an idle worker,
     * the WorkDispatcher will be created to send the
     * task to that worker.
     */
    public void run() {
    	WorkerTaskUnit workerTaskUnit = null;
        try {
            System.out.printf("task id: %d begin.%n", taskID);
            workerTaskUnit =  new WorkerTaskUnit(taskID, programName,
                arguments, datafileName, outputFileName);

            // get the work service from resource.
            WorkService workService = resource.getWorkService();

            if(workService == null) {
                System.out.println("WorkService NULL!!!");
            }

            // read program file
            readProgramFile(workerTaskUnit);
            // read data file
            readDataFile(workerTaskUnit);

            System.out.println("authCode: " + resource.getAuthCode());
            // call execute method of WorkService which is a remote object on workers
            ResultUnit workResultUnit
                    = workService.execute(workerTaskUnit, resource.getAuthCode());

            
            if(workResultUnit != null) {
                // get the copy of output file
                byte[] buffer = workResultUnit.getOutputFile();
                this.elapsedTime = workResultUnit.getElapsedTime();
                this.totalCost = workResultUnit.getTotalCost();
                IO.writeFile(getOutputFileName(), buffer);

                System.out.printf("task id: %d finish.%n", taskID);
            }
            else{
            	 System.out.printf("task id: %d is canceled.%n", taskID);

            }
        }
        catch(Exception ex)
        {
            System.out.println("##########################" +
            		"Worker" + resource.getID() + " cannot work!!!!!!!!!!!!!!"
            		+ "TaskID = " + workerTaskUnit.getWorkUnitID());
            
            ex.printStackTrace();

            resource.setAvailable(false);
            // reschedule the worker
            //stop dispatcher
            resource.stopWorkerDispatcher();

            //ex.printStackTrace();
        }
    }

    public Resource getSpecifiedResource(){
        return this.resource;
    }

	public float getElapsedTime() {
		return elapsedTime;
	}

	public float getTotalCost() {
		return totalCost;
	}
}
